const ErrorVal = 10e-6;
let echlonStep = 0,
  records = [],
  selMats = [];
let cmd = "";
let M = Array(4);
let precision = Number(document.querySelector("#selPre").value);
let genN = Number(document.querySelector("#selGenN").value);
let drawCord = true,
  inFraction = false,
  recordSteps = false;
let rcdDiv = document.querySelector("#recordDiv");
window.addEventListener("load", function () {
  console.log("start js");
  document.querySelector("#selPre").onchange = (e) =>
    (precision = Number(e.target.value));
  document.querySelector("#selGenN").onchange = (e) =>
    (genN = Number(e.target.value));
  initMat();
  updateSelection();
  initMathMat();
  // initDivEditor();
  // setInitValue();
});

function initMat() {
  M[0] = new Mat();
  for (let i = 1; i < 4; i++) M[i] = new Mat(0, 0, i);
}
function initDivEditor() {
  let de = document.getElementById("divEditor");
  // de.addEventListener("paste", (e) => {
  //   e.preventDefault();
  //   const text = (e.clipboardData || window.clipboardData).getData(
  //     "text/plain"
  //   );
  //   document.execCommand("insertText", false, text);
  // });
  // de.addEventListener("click", () => {
  //   de.focus();
  // });
  de.addEventListener("input", () => {
    if (de.innerHTML === "") de.innerHTML = "";
    console.log("inputing");
  });
  // de.addEventListener("focus", () => {
  //   if (de.innerHTML === "") de.innerHTML = "";
  // });
  // de.addEventListener("blur", () => {
  //   if (de.innerHTML === "") de.innerHTML = "";
  // });
}

function initMathMat() {
  let mm = document.getElementById("#matDefault");
}
class Mat {
  constructor(m = 0, n = 0, ID) {
    this.cRow = m;
    if (n == 0) this.cCol = m;
    else this.cCol = n;
    this.data = Array(m)
      .fill(0)
      .map(() => Array(n).fill(0));
    this.vectors = null;
    if (ID != null) {
      this.input = document.getElementById("input" + ID);
      this.table = document.getElementById("table" + ID);
      this.tbody = this.table.querySelector("tbody");
      this.name = "M" + ID;
    } else {
      this.input = document.getElementById("input0");
      this.table = document.getElementById("table0");
      this.name = "Matrix";
    }
  }
  // row multiply:
  static _rowMul(row, factor) {
    for (let j = 0; j < this.cCol; j++)
      this.data[row][j] = Mat._eMul(this.data[row][j], factor);
    cmd = `M${this.name}[${row}] *= ${factor};\n`;
  }
  // row add:
  static _addA2B(rowA, rowB) {
    for (let j = 0; j < this.cCol; j++)
      this.data[rowB][j] = Mat._eAdd(this.data[rowA][j], this.data[rowB][j]);
  }
  // element multiply and divide:
  static _eMul(x1, x2) {
    if (!(isNaN(x1) || isNaN(x2)))
      return Math.abs(x1 * x2) > ErrorVal ? x1 * x2 : 0;
    else if (x1 == 0 || x2 == 0) return 0;
    else {
      x1 = isNaN(x1) ? x1 : round(x1);
      x2 = isNaN(x2) ? x2 : round(x2);
      x1 = new CalcString(x1.toString());
      x2 = new CalcString(x2.toString());
      return x1.strOp("*", x2);
    }
  }
  static _eDiv(x1, x2) {
    if (!(isNaN(x1) || isNaN(x2))) {
      if (x1 == 0) {
        return 0;
      }
      if (x2 == 0) return new CalcString(x1 + "/" + "0");
      else return Math.abs(x1 / x2) > ErrorVal ? x1 / x2 : 0;
    } else {
      x1 = isNaN(x1) ? x1 : round(x1);
      x2 = isNaN(x2) ? x2 : round(x2);
      x1 = new CalcString(x1.toString());
      x2 = new CalcString(x2.toString());
      return x1.strOp("/", x2);
    }
  }
  // element add and sub:
  static _eAdd(x1, x2) {
    if (!(isNaN(x1) || isNaN(x2)))
      return Math.abs(x1 + x2) > ErrorVal ? x1 + x2 : 0;
    else {
      if (x1 == 0) {
        return x2;
      }
      if (x2 == 0) {
        return x1;
      }
      x1 = isNaN(x1) ? x1 : round(x1);
      x2 = isNaN(x2) ? x2 : round(x2);
      x1 = new CalcString(x1.toString());
      x2 = new CalcString(x2.toString());
      return x1.strOp("+", x2);
    }
  }
  static _eSub(x1, x2) {
    if (!(isNaN(x1) || isNaN(x2)))
      return Math.abs(x1 - x2) > ErrorVal ? x1 - x2 : 0;
    else {
      if (x1 == x2) {
        return 0;
      }
      // if (x1 == 0) {
      // 	if (x2[0] == "-")
      // 		return x2.substring(1)
      // 	else
      // 		return new CalcString("-" + x2.toString())
      // }
      if (x2 == 0) {
        return x1;
      }
      x1 = isNaN(x1) ? x1 : round(x1);
      x2 = isNaN(x2) ? x2 : round(x2);
      x1 = new CalcString(x1.toString());
      x2 = new CalcString(x2.toString());
      return x1.strOp("-", x2);
    }
  }
  static _eCmp(x1, x2) {
    if (!(isNaN(x1) || isNaN(x2))) return math.abs(x1) - math.abs(x2);
    else return 0;
  }
  swapRow(r1, r2) {
    let tmp = this.data[r1];
    this.data[r1] = this.data[r2];
    this.data[r2] = tmp;
  }
  rrForward() {
    //rr for row reduce
    this.LULower = new Mat(this.cRow, this.cRow);
    this.permutation = [];
    this.pivotPosition = [];
    this.freeColumns = [];
    let r1 = 0;
    let c1 = 0;
    let r2;
    let c2;
    let max;
    let factor = 1;
    let mle; //max leading element
    echlonStep = 0;
    if (recordSteps) {
      console.log("rrforward start:");
      this.printMat("current matrix");
    }
    let ffRightBound;
    if (this.hasOwnProperty("originalCCol"))
      ffRightBound = this.originalCCol; //matrix is augmented
    else ffRightBound = this.cCol;
    while (r1 < this.cRow && c1 < ffRightBound) {
      mle = this.data[r1][c1];
      max = r1;
      for (r2 = r1 + 1; r2 < this.cRow; r2++)
        if (Mat._eCmp(this.data[r2][c1], mle) > 0) {
          mle = this.data[r2][c1];
          max = r2; //max leading row
        }
      if (mle == 0) {
        this.freeColumns.push(c1);
        c1++;
      } else {
        if (max != r1) {
          this.swapRow(max, r1);
          this.LULower.swapRow(max, r1);
          this.permutation.push([r1, max]);
        }
        for (r2 = r1 + 1; r2 < this.cRow; r2++) {
          factor = Mat._eDiv(this.data[r2][c1], this.data[r1][c1]);
          this.LULower.data[r2][r1] = factor;
          for (c2 = c1; c2 < this.cCol; c2++)
            this.data[r2][c2] = Mat._eSub(
              this.data[r2][c2],
              Mat._eMul(this.data[r1][c2], factor)
            );
          if (recordSteps) this.printMat(factor);
        }
        this.pivotPosition.push(c1);
        r1++;
        c1++;
      }
    }
    for (; c1 < ffRightBound; c1++) this.freeColumns.push(c1);
    for (let i = 0; i < this.cRow; i++) this.LULower.data[i][i] = 1;
    this.LUUpper = this.copyMat();
  }
  rrBackward() {
    echlonStep = 0;
    if (recordSteps) {
      console.log("rrbackward:");
      this.printMat("current matrix");
    }
    for (let r1 = this.pivotPosition.length - 1; r1 >= 0; r1--) {
      let c1 = this.pivotPosition[r1];
      let factor = Mat._eDiv(1, this.data[r1][c1]);
      for (let c2 = c1; c2 < this.cCol; c2++)
        this.data[r1][c2] = Mat._eMul(this.data[r1][c2], factor);
      for (let r2 = r1 - 1; r2 >= 0; r2--) {
        factor = this.data[r2][c1];
        for (let c2 = c1; c2 < this.cCol; c2++)
          this.data[r2][c2] = Mat._eSub(
            this.data[r2][c2],
            Mat._eMul(this.data[r1][c2], factor)
          );
      }
      if (recordSteps) this.printMat(factor);
    }
  }
  solveAxEqualB_LUFac({ b = 0, showLUTable = true, showXResult = true } = {}) {
    if (b == 0) {
      b = new Mat(this.cRow, 1);
      for (let i = 0; i < this.cRow; i++) b.data[i][0] = 0;
    }
    if (this.cRow != b.cRow) {
      alert("mismatched count of rows");
      return;
    }
    let A = this.copyMat();
    A.LUFactorization({ showLUTable: showLUTable });
    let y = b.copyMat();
    for (let swap of A.permutation) {
      let [r1, r2] = [...swap];
      y.swapRow(r1, r2);
    }
    let [L, U] = [A.LULower, A.LUUpper];
    for (let r1 = 0; r1 < L.cRow; r1++) {
      let c1 = r1;
      for (let r2 = r1 + 1; r2 < L.cRow; r2++)
        for (let c2 = 0; c2 < y.cCol; c2++)
          y.data[r2][c2] = Mat._eSub(
            y.data[r2][c2],
            Mat._eMul(L.data[r2][c1], y.data[r1][c2])
          );
    }
    if (showLUTable) y.genTable("y (L*y=b)");
    //the next for loop will work only when pivot position less than row count i.e. free variable exists:
    for (let i = A.pivotPosition.length; i < U.cRow; i++)
      if (y.data[i][0] != 0) {
        alert("equation inconsistant: no solution");
        return null;
      }
    A.joinMat(y);
    A.rrBackward();
    let x = new Vector(this.cCol);
    for (let i = 0; i < this.cRow; i++) x.data[i][0] = A.data[i].at(-1);
    x.freeX = Array.from(A.freeColumns);
    for (let i of A.freeColumns) x.data[i][0] = `x${i + 1}`;
    for (let r = A.pivotPosition.length - 1; r >= 0; r--) {
      for (let j = A.pivotPosition[r] + 1; j < A.cCol - 1; j++) {
        x.data[r][0] = Mat._eSub(
          x.data[r][0],
          Mat._eMul(A.data[r][j], x.data[j][0])
        );
      }
    }
    if (showXResult) x.genTable("x (U*x=y)");
    return x;
  }
  genPermutation() {
    if (this.permutation) {
      let pMat = new MatIN(this.cRow);
      for (let p of this.permutation) pMat.swapRow(p[0], p[1]);
      return pMat;
    }
  }
  echlon() {
    this.rrForward();
    this.rrBackward();
  }
  joinMat(other) {
    if (this.cRow != other.cRow) {
      alert("different row count");
      return;
    }
    this.originalCCol = this.cCol;
    for (let i = 0; i < this.cRow; i++)
      this.data[i] = this.data[i].concat(other.data[i]);
    this.cCol += other.cCol;
  }
  transpose() {
    let resultMat = new Mat(this.cCol, this.cRow);
    for (let i = 0; i < this.cRow; i++)
      for (let j = 0; j < this.cCol; j++)
        resultMat.data[j][i] = this.data[i][j];
    return resultMat;
  }
  inverse() {
    if (this.cRow != this.cCol) {
      alert(this.name + " not a square mat");
      return;
    }
    let resultMat = this.copyMat(true);
    let INMat = new MatIN(this.cRow);
    resultMat.joinMat(INMat);
    resultMat.echlon();
    let testMat = resultMat.copyMat(false, [
      [0, resultMat.cRow],
      [0, resultMat.cCol / 2],
    ]);
    if (testMat.isIN()) {
      resultMat = resultMat.copyMat(true, [
        [0, resultMat.cRow],
        [resultMat.cCol / 2, resultMat.cCol],
      ]);
      return resultMat;
    } else alert(this.name + " uninvertible");
  }
  matMultiply(otherM) {
    let resultMat;
    let factor = null;
    if (this.cRow == 1 && this.cCol == 1) {
      factor = this.data[0][0];
      resultMat = otherM.copyMat(true);
    } else if (otherM.cRow == 1 && otherM.cCol == 1) {
      factor = otherM.data[0][0];
      resultMat = this.copyMat(true);
    }
    if (factor != null) {
      //factor == 0 is possible
      for (let i = 0; i < this.cRow; i++)
        for (let j = 0; j < this.cCol; j++)
          resultMat.data[i][j] = Mat._eMul(factor, resultMat.data[i][j]);
      return resultMat;
    }
    if (this.cCol != otherM.cRow) {
      alert("matrix unmatched");
      return null;
    } else {
      resultMat = new Mat(this.cRow, otherM.cCol);
      for (let i = 0; i < this.cRow; i++)
        for (let j = 0; j < otherM.cCol; j++) {
          let x = 0;
          for (let k = 0; k < this.cCol; k++)
            x = Mat._eAdd(x, Mat._eMul(this.data[i][k], otherM.data[k][j]));
          resultMat.data[i][j] = x;
        }
    }
    return resultMat;
  }
  matAddSub(otherM, op) {
    let resultMat;
    if (this.cCol != otherM.cCol || this.cRow != otherM.cRow) {
      alert("matrix unmatched");
      return null;
    } else {
      resultMat = new Mat(this.cRow, this.cCol);
      if (op == "+" || op == "add") {
        for (let i = 0; i < this.cRow; i++)
          for (let j = 0; j < this.cCol; j++)
            resultMat.data[i][j] = Mat._eAdd(
              this.data[i][j],
              otherM.data[i][j]
            );
      } else if (op == "-" || op == "sub") {
        for (let i = 0; i < this.cRow; i++)
          for (let j = 0; j < this.cCol; j++)
            resultMat.data[i][j] = Mat._eSub(
              this.data[i][j],
              otherM.data[i][j]
            );
      }
    }
    return resultMat;
  }
  matScale(scaler, inplace = false) {
    if (!inplace) {
      let result = new Mat(this.cRow, this.cCol);
      for (let i = 0; i < this.cRow; i++)
        for (let j = 0; j < this.cCol; j++)
          result.data[i][j] = Mat._eMul(scaler, this.data[i][j]);
      return result;
    } else {
      for (let i = 0; i < this.cRow; i++)
        for (let j = 0; j < this.cCol; j++)
          this.data[i][j] = Mat._eMul(scaler, this.data[i][j]);
    }
  }
  matNormalize(inplace = false) {
    let resultMat;
    if (inplace) resultMat = this;
    else resultMat = this.copyMat(true);
    resultMat.vectorize();
    for (let i = 0; i < resultMat.vectors.length; i++)
      resultMat.vectors[i].vecNormalize(true);
    resultMat.combineVectors();
    return resultMat;
  }
  LUFactorization({ showLUTable = true } = {}) {
    this.rrForward();
    if (showLUTable) {
      this.LULower.genTable("lower " + this.name);
      this.LUUpper.genTable("upper " + this.name);
      this.genPermutation().genTable("permutation " + this.name);
    }
  }
  QRFactorization() {
    let Q = this.orthonormalBasis();
    let R = Q.transpose().matMultiply(this);
    return [Q, R];
  }
  DetRec() {
    if (this.cCol != this.cRow) {
      alert(this.name + " not a square matrix");
      return;
    }
    if (this.cCol > 10) {
      alert(
        this.name + " larger than 10 (for recursive determinant calculation)"
      );
      return;
    }
    if (this.cRow == 1) return this.data[0][0];
    if (this.cRow == 2)
      return Mat._eSub(
        Mat._eMul(this.data[0][0], this.data[1][1]),
        Mat._eMul(this.data[1][0], this.data[0][1])
      );
    let det = 0;
    let [vInRow, vInCol] = this.valueCountInMat(0);
    let [maxRow, maxCol] = [vInRow.findMaxNdx(), vInCol.findMaxNdx()];
    if (vInRow[maxRow] >= vInCol[maxCol])
      for (let j = 0; j < this.cCol; j++) {
        if (this.data[maxRow][j] != 0) {
          let subMat = this.crossOutMat(maxRow, j);
          let [a, b, c] = [
            Math.pow(-1, j + maxRow),
            this.data[maxRow][j],
            subMat.DetRec(),
          ];
          det = Mat._eAdd(det, Mat._eMul(Mat._eMul(a, b), c));
        }
      }
    else
      for (let i = 0; i < this.cRow; i++)
        if (this.data[i][maxCol] != 0) {
          let subMat = this.crossOutMat(i, maxCol);
          let [a, b, c] = [
            Math.pow(-1, i + maxCol),
            this.data[i][maxCol],
            subMat.DetRec(),
          ];
          det = Mat._eAdd(det, Mat._eMul(Mat._eMul(a, b), c));
        }
    return det;
  }
  DetByLU() {
    if (this.cCol != this.cRow) {
      alert(this.name + " not a square matrix");
      return;
    }
    this.rrForward();
    let det = this.data.length > 0 ? 1 : 0;
    for (let i = 0; i < this.cRow; i++)
      det = Mat._eMul(det, this.LUUpper.data[i][i]);
    det = Mat._eMul(det, Math.pow(-1, this.permutation.length));
    return det;
  }
  adjugate() {
    if (this.cCol != this.cRow) {
      alert(this.name + " not a square matrix");
      return;
    }
    let result = new Mat(this.cRow, this.cCol);
    for (let i = 0; i < this.cRow; i++)
      for (let j = 0; j < this.cCol; j++) {
        let subMat = this.crossOutMat(i, j);
        let d = subMat.DetByLU();
        result.data[j][i] = Mat._eMul(Math.pow(-1, i + j), d);
      }
    return result;
  }
  async findEigenvalue() {
    if (this.cCol != this.cRow) {
      alert(this.name + " not a square matrix");
      return;
    }
    this.eigenvalueTable = new Mat(this.cRow, 1);
    this.eigenvalues = [];
    this.eigenvalueDiagonal = new MatIN(this.cRow);
    if (this.isUpperTriangular() || this.isLowerTriangular()) {
      let evList = [];
      for (let i = 0; i < this.cRow; i++) evList.push(this.data[i][i]);
      evList = evList.sort((a, b) => b - a);
      for (let i = 0; i < this.cRow; i++) {
        this.eigenvalueTable.data[i][0] = evList[i];
        this.eigenvalueDiagonal.data[i][i] = evList[i];
        this.eigenvalues.push(evList[i]);
      }
      return this.eigenvalueTable;
    }
    let lambdaMat = genDiagonal(this.cRow, "λ", false);
    let det = _M1AddSubM2(this, lambdaMat, "-").DetRec()._expand().toString();
    if (/[a-zA-Z\u4e00-\u9fa5]/.test(det)) {
      return new Error("symbols not supported yet");
    }
    if (this.cRow == 2) {
      let matched = det.match(
        /(?<c>[+-]?\d*\.?\d*(?=[+-]))(?<b>[+-]?\d*\.?\d*)\*?λ(?=[+-])(?<a>[+-]?\d*\.?\d*)\*?λ\*λ/
      );
      let [a, b, c] = [
        parseFloat(matched.groups.a) || 1,
        parseFloat(matched.groups.b) || 1,
        parseFloat(matched.groups.c) || 0,
      ];
      let delta = b * b - 4 * a * c;
      if (delta >= 0) {
        let [λ1, λ2] = [
          (-b + Math.sqrt(delta)) / (2 * a),
          (-b - Math.sqrt(delta)) / (2 * a),
        ].sort((a, b) => b - a);
        this.eigenvalues = [λ1, λ2];
        [
          this.eigenvalueDiagonal.data[0][0],
          this.eigenvalueDiagonal.data[1][1],
        ] = [λ1, λ2];
        [this.eigenvalueTable.data[0][0], this.eigenvalueTable.data[1][0]] = [
          λ1,
          λ2,
        ];
        return this.eigenvalueTable;
      } else return new Error("delta < 0: complex eigenvlues exist.");
    } else {
      console.log("fetching:");
      let token = localStorage.getItem("token") || "";
      if (!token) return new Error("Token not found");
      token = "Bearer " + token;
      try {
        let response = await fetch("http://127.0.0.1:8000/eqsolver", {
          method: "POST",
          headers: { "Content-Type": "application/json", Authorization: token },
          body: JSON.stringify({ ctt: det }),
        });
        const data = await response.json();
        if (!response.ok) {
          throw new Error(data.detail);
        }
        if (data && data.allRealRoots) {
          this.eigenvalues = data.roots;
          for (let i = 0; i < this.eigenvalues.length; i++) {
            this.eigenvalueDiagonal.data[i][i] = this.eigenvalues[i];
            this.eigenvalueTable.data[i][0] = this.eigenvalues[i];
          }
          return this.eigenvalueTable;
        } else
          throw new Error("Message from server: complex eigenvlues exist.");
      } catch (error) {
        console.log("error:", error);
        return error;
      }
    }
  }
  findEigenvector() {
    this.eigenVectors = new Mat(this.cRow, this.cCol);
    this.freeXinEV = [];
    for (let j = 0; j < this.eigenvalues.length; j++) {
      let tmpMat = genDiagonal(this.cRow, this.eigenvalues[j], false);
      let characteristicMat = _M1AddSubM2(this, tmpMat, "-");
      let x = characteristicMat.solveAxEqualB_LUFac({
        showLUTable: false,
        showXResult: false,
      });
      for (let i = 0; i < this.cRow; i++)
        this.eigenVectors.data[i][j] = x.data[i][0];
      this.freeXinEV.push(x.freeX);
    }
    this.findNumericEigenvector();
  }
  findNumericEigenvector() {
    //e.g: for duplicated eigenvalues: 3 evectors contain "x1-2*x3+0.5*x6";
    // x1,x3,x6=[[1,0,0],[0,1,0],[0,0,1]] in turn and evaluate them.
    this.numericEigenvectors = new Mat(this.cRow, this.cCol);
    let ndx = 0;
    for (let j = 0; j < this.cCol; j++) {
      let obj = {};
      for (let i of this.freeXinEV[j]) obj[`x${i + 1}`] = 0; //declare x1,x3,x6 in runtime
      if (j == 0) obj[`x${this.freeXinEV[0][0] + 1}`] = 1;
      else {
        if (j == this.freeXinEV[ndx].length + ndx) ndx = j;
        obj[`x${this.freeXinEV[j][j - ndx] + 1}`] = 1; // crazy man : such as x3, x6.. = 1 in turn.
      }
      for (let i = 0; i < this.cRow; i++) {
        if (!isNaN(this.eigenVectors.data[i][j]))
          this.numericEigenvectors.data[i][j] = this.eigenVectors.data[i][j];
        else {
          let s = this.eigenVectors.data[i][j];
          s = s.replaceAll("x", "obj.x");
          this.numericEigenvectors.data[i][j] = eval(s);
        }
      }
    }
    this.numericEigenvectors.vectorize();
  }
  symbolToNumber(x) {
    let resultMat = new Mat(x.cRow, x.freeX.length);
    let tmpMat = new MatIN(x.freeX.length);
    for (let j = 0; j < tmpMat.cRow; j++) {
      let obj = {};
      for (let i of x.freeX) obj[`x${i + 1}`] = 0;
      obj[`x${x.freeX[j] + 1}`] = 1;
      for (let i = 0; i < x.cRow; i++) {
        if (!isNaN(x.data[i][0])) resultMat.data[i][j] = x.data[i][0];
        else {
          let s = x.data[i][0];
          s = s.replaceAll("x", "obj.x");
          resultMat.data[i][j] = eval(s);
        }
      }
    }
    resultMat.vectorize();
    return resultMat;
  }
  async SVD() {
    try {
      let AT = this.transpose();
      let ATA = AT.matMultiply(this);
      let evs = await ATA.findEigenvalue();
      ATA.findEigenvector();
      let rank = ATA.eigenvalues.filter((v) => Math.abs(v) > ErrorVal).length;
      this.SV = new Mat(this.cRow, this.cCol);
      for (let i = 0; i < rank; i++)
        this.SV.data[i][i] = Math.sqrt(evs.data[i][0]);
      let v = ATA.numericEigenvectors.copyMat(true, [
        [0, ATA.cRow],
        [0, rank],
      ]);
      this.rightSV = ATA.numericEigenvectors.matNormalize(false).transpose();
      this.leftSV = this.matMultiply(v);
      if (rank < this.cRow) {
        let UT = this.leftSV.transpose();
        let x = UT.solveAxEqualB_LUFac({
          showLUTable: false,
          showXResult: false,
        });
        let otherU = this.symbolToNumber(x);
        this.leftSV.joinMat(otherU);
      }
      this.leftSV.matNormalize(true);
      return true;
    } catch (err) {
      return err;
    }
  }

  //vector related:
  vectorize() {
    this.vectors = Array(this.cCol)
      .fill(0)
      .map(() => new Vector(this.cRow));
    for (let j = 0; j < this.cCol; j++)
      for (let i = 0; i < this.cRow; i++)
        this.vectors[j].data[i][0] = this.data[i][j];
    return this.vectors;
  }
  combineVectors() {
    for (let i = 0; i < this.cRow; i++)
      for (let j = 0; j < this.cCol; j++)
        this.data[i][j] = this.vectors[j].data[i][0];
  }
  orthogonalBasis() {
    let ob = this.copyMat(true);
    ob.vectorize();
    for (let j = 1; j < ob.cCol; j++) {
      for (let k = 0; k < j; k++) {
        let p = ob.vectors[j].projectOnVec(ob.vectors[k]);
        ob.vectors[j] = ob.vectors[j].vectorAddSub(p, "-");
      }
    }
    ob.combineVectors();
    return ob;
  }
  orthonormalBasis() {
    let result = this.orthogonalBasis();
    for (let v of result.vectors) v.vecNormalize(true);
    result.combineVectors();
    return result;
  }

  //misc methods:
  copyMat(deep = false, range = null) {
    let copyMat;
    let r1;
    let r2;
    let c1;
    let c2;
    if (range != null) [[r1, r2], [c1, c2]] = [...[...range]];
    else
      [[r1, r2], [c1, c2]] = [
        [0, this.cRow],
        [0, this.cCol],
      ];
    copyMat = new Mat(r2 - r1, c2 - c1);
    if (deep == true)
      for (let i = 0; i < r2 - r1; i++)
        for (let j = 0; j < c2 - c1; j++)
          copyMat.data[i][j] = this.data[r1 + i][c1 + j];
    else
      for (let i = 0; i < r2 - r1; i++)
        copyMat.data[i] = this.data[r1 + i].slice(c1, c2);
    return copyMat;
  }
  valueCountInRC(v, rc = "row", n) {
    let count = 0;
    if (rc == "row")
      for (let i = 0; i < this.cCol; i++)
        if (this.data[n][i] == v) count++;
        else if (rc == "col")
          for (let i = 0; i < this.cRow; i++) if (this.data[i][n] == v) count++;
    return count;
  }
  valueCountInMat(v) {
    let vInRow = Array(this.cRow).fill(0);
    let vInCol = Array(this.cCol).fill(0);
    for (let i = 0; i < this.cRow; i++)
      for (let j = 0; j < this.cCol; j++)
        if (this.data[i][j] == v) {
          vInRow[i]++;
          vInCol[j]++;
        }
    return [vInRow, vInCol];
  }
  crossOutMat(i, j) {
    let result = this.copyMat(true);
    for (let i = 0; i < this.cRow; i++) result.data[i].splice(j, 1);
    result.data.splice(i, 1);
    result.cRow--;
    result.cCol--;
    return result;
  }
  isUpperTriangular() {
    for (let i = 1; i < this.cRow; i++)
      for (let j = 0; j < i && j < this.cCol; j++)
        if (isNaN(this.data[i][j]) || Math.abs(this.data[i][j]) > ErrorVal)
          return false;
    return true;
  }
  isLowerTriangular() {
    for (let i = 0; i < this.cRow; i++)
      for (let j = i + 1; j < this.cCol; j++)
        if (isNaN(this.data[i][j]) || Math.abs(this.data[i][j]) > ErrorVal)
          return false;
    return true;
  }
  isIN() {
    for (let i = 0; i < this.cRow; i++)
      for (let j = 0; j < this.cCol; j++)
        if (
          (i == j && Math.abs(this.data[i][j] - 1) > ErrorVal) ||
          (i != j && Math.abs(this.data[i][j] - 0) > ErrorVal)
        )
          return false;
    return true;
  }

  // html related:
  parseInput() {
    this.cCol = this.cRow = 0;
    this.data = this.input.value;
    if (this.data.length == 0) return;
    this.data = this.data.replaceAll(/[\n\t\s]+$/g, "");
    this.data = this.data.replaceAll(/[\t\s]+\n/g, "\n");
    this.data = this.data.replaceAll(/\n[\t\s]+/g, "\n");
    this.data = this.data.replaceAll(/[\t ]+/g, "\t");
    this.data = this.data.split("\n");
    for (let i = 0; i < this.data.length; i++) {
      this.data[i] = this.data[i].split("\t");
      for (let j = 0; j < this.data[i].length; j++) {
        let str = this.data[i][j];
        if (isNaN(str)) {
          try {
            let reTri = /(\bsin|\bcos|\btan|\bctan|\bsec|\bcsc)\(?(\d+)\)?/g;
            let reSqrt = /(√)(\(?\d*\.?\d*\)?)/g;
            if (reTri.test(str))
              str = str.replace(reTri, (matched, p1, p2) => {
                return `${p1}(${p2} deg)`;
              });
            if (reSqrt.test(str))
              str = str.replace(reSqrt, (matched, p1, p2) => {
                return `sqrt(${p2})`;
              });
            this.data[i][j] = math.evaluate(str);
          } catch {
            console.log("parseInput: undefined symbol");
          }
        } else this.data[i][j] = Number(str);
      }
      if (this.data[i].length > this.cCol) this.cCol = this.data[i].length;
    }
    this.cRow = this.data.length;
    for (let i of $("img[src='pic.png']")) i.classList.remove("dspInlineblock");
  }
  fillTable() {
    this.table.setAttribute("data-fixed", true);
    this.tbody.replaceChildren();
    for (let i = 0; i < this.cRow; i++) {
      let newRow = document.createElement("tr");
      this.tbody.appendChild(newRow);
      for (let j = 0; j < this.cCol; j++) {
        let newCell = document.createElement("td");
        let txt = this.data[i][j];
        try {
          txt = math.evaluate(txt.toString()).toString(); // if txt is a complex number
        } catch {}
        if (!isNaN(txt) && !Number.isInteger(Number(txt))) {
          //i.e. txt=0.3
          if (inFraction)
            txt = math.format(math.fraction(txt), { fraction: "ratio" });
          else txt = round(txt);
        }
        newCell.innerHTML = txt;
        newRow.appendChild(newCell);
      }
    }
  }
  genTable(caption = "Result") {
    let div = document.getElementById("resultDiv");
    this.table = document.createElement("table");
    this.table.className = "mainTable dspInlineblock";
    this.table.setAttribute("data-sel", "0");
    div.appendChild(this.table);
    let cap = document.createElement("caption");
    cap.innerText = caption;
    // cap.setAttribute("contenteditable", "true");
    let lb = document.createElement("label");
    lb.innerHTML = "&#10004";
    lb.className = "tabbed dspNone";
    cap.appendChild(lb);
    this.table.appendChild(cap);
    this.tbody = document.createElement("tbody");
    this.table.appendChild(this.tbody);
    for (let i = 0; i < this.cRow; i++) {
      let newRow = document.createElement("tr");
      this.tbody.appendChild(newRow);
      for (let j = 0; j < this.cCol; j++) {
        let newCell = document.createElement("td");
        let txt = this.data[i][j];
        try {
          txt = math.evaluate(txt.toString()).toString(); // if txt is a complex number
        } catch {}
        if (isNaN(txt) || Number.isInteger(Number(txt))) {
        } else if (inFraction)
          txt = math.format(math.fraction(txt), { fraction: "ratio" });
        else txt = round(txt);
        newCell.innerHTML = txt;
        newRow.appendChild(newCell);
      }
    }
    addTableEvent(this.table);
  }
  updateTable() {
    this.clearTable();
    this.fillTable();
  }
  clearTable() {
    this.tbody.replaceChildren();
  }
  removeCopy() {
    this.table.parentNode.removeChild(this.cpTable);
  }
  hide() {
    if (this.input) this.input.parentNode.classList.add("dspNone");
    this.table.classList.add("dspNone");
  }
  show() {
    if (this.input.id != "input0" && this.name != "M0")
      this.input.parentNode.classList.remove("dspNone");
    this.table.classList.remove("dspNone");
  }
  printMat(factor = null) {
    let str = "";
    for (let i = 0; i < this.cRow; i++) {
      for (let j = 0; j < this.cCol; j++) {
        str += this.data[i][j].toString() + "\t";
      }
      str += "\n";
    }
    if (factor != null) {
      rcdDiv.innerHTML += str;
      console.log("step:", echlonStep, "factor:", factor.toString());
    } else console.log("step:", echlonStep);
    console.log(str);
    echlonStep++;
  }
  drawVectors() {
    let txt = "";
    this.vectorize();
    for (let i = 0; i < this.cCol; i++) {
      let v = this.vectors[i].data;
      if (drawCord)
        txt =
          round(v[0][0].toString(), 1) + ", " + round(v[1][0].toString(), 1);
      drawVec([0, 0], v, true, txt);
    }
  }
  drawShape() {
    let txt = "";
    if (!this.vectors) this.vectorize();
    if (this.cCol > 1)
      for (let i = 1; i < this.cCol; i++) {
        let v1 = this.vectors[i - 1].data,
          v2 = this.vectors[i].data;
        if (drawCord)
          txt =
            round(v2[0][0].toString(), 1) +
            ", " +
            round(v2[1][0].toString(), 1);
        drawVec(v1, v2, false, txt);
      }
    let v1 = this.vectors[this.cCol - 1].data,
      v2 = this.vectors[0].data;
    if (drawCord)
      txt =
        round(v2[0][0].toString(), 1) + ", " + round(v2[1][0].toString(), 1);
    drawVec(v1, v2, false, txt);
  }
} // class end

class MatIN extends Mat {
  constructor(n) {
    super(n, n);
    for (let i = 0; i < n; i++) this.data[i][i] = 1;
  }
}

class Vector extends Mat {
  constructor(n) {
    super(n, 1);
  }
  fill(value) {
    for (let i = 0; i < this.cRow; i++) this.data[i][0] = value;
  }
  innerProduct(other) {
    if (other.cRow != this.cRow) {
      alert("vectors not match");
      return;
    }
    let sum = 0;
    for (let i = 0; i < this.cRow; i++)
      sum = Mat._eAdd(sum, Mat._eMul(this.data[i][0], other.data[i][0]));
    return sum;
  }
  selfInnerProduct() {
    let sum = 0;
    for (let i = 0; i < this.cRow; i++)
      sum = Mat._eAdd(sum, Mat._eMul(this.data[i][0], this.data[i][0]));
    return sum;
  }
  vectorLength() {
    return Math.sqrt(this.selfInnerProduct());
  }
  projectOnVec(other) {
    let r = Mat._eDiv(this.innerProduct(other), other.selfInnerProduct());
    let result = other.vectorScale(r);
    return result;
  }
  projectOnSpace(space = new Mat()) {
    let ob = space.orthogonalBasis();
    ob.vectorize();
    let projVector = new Vector();
  }
  vecNormalize(inplace = false) {
    let scaled = this.vectorScale(1 / this.vectorLength(), inplace);
    if (!inplace) return scaled;
  }
  vectorScale(scaler, inplace = false) {
    if (!inplace) {
      let result = new Vector(this.cRow);
      for (let i = 0; i < this.cRow; i++)
        result.data[i][0] = Mat._eMul(scaler, this.data[i][0]);
      return result;
    } else
      for (let i = 0; i < this.cRow; i++)
        this.data[i][0] = Mat._eMul(scaler, this.data[i][0]);
  }
  vectorAddSub(other, op) {
    let m = this.matAddSub(other, op);
    return m.vectorize()[0];
  }
}
function round(number, p = precision) {
  if (Math.abs(number) > ErrorVal)
    return Math.round(+number + "e" + p) / Math.pow(10, p);
  else return 0;
}
function updateSelection() {
  selMats = [];
  for (let i of document.getElementsByName("selTable"))
    if (i.checked) {
      selMats.push(i.value);
      M[i.value].parseInput();
      M[i.value].fillTable();
      M[i.value].show();
    } else M[i.value].hide();
}
function genIN(n) {
  if (n == null) n = genN;
  let resultMat = new MatIN(n);
  resultMat.genTable("IN");
  return resultMat;
}
function genDiagonal(n, value, show = true) {
  if (n == null) n = genN;
  let resultMat = new MatIN(n);
  for (let i = 0; i < resultMat.cRow; i++) resultMat.data[i][i] = value;
  if (show) resultMat.genTable("Diagonal of " + value);
  return resultMat;
}
function genRandom(genInt = false, n) {
  if (n == null) n = genN;
  let resultMat = new Mat(n);
  if (genInt) precision = 0;
  for (let i = 0; i < n; i++)
    for (let j = 0; j < n; j++)
      resultMat.data[i][j] = round(20 * (Math.random() - 0.5));
  resultMat.genTable("Random");
  precision = Number(document.querySelector("#selPre").value);
  return resultMat;
}
function genRandomLT(n) {
  if (n == null) n = genN;
  let resultMat = new Mat(n);
  for (let j = 0; j < n; j++) {
    let sum = 0;
    for (let i = 0; i < n; i++) {
      resultMat.data[i][j] = round(Math.random() * (1 - sum));
      sum += resultMat.data[i][j];
    }
  }
  resultMat.genTable("Random");
  return resultMat;
}
function clearAll() {
  document.getElementById("resultDiv").replaceChildren();
}
function clearSelected() {
  let tbs = document
    .getElementById("resultDiv")
    .querySelectorAll("table.selected");
  for (tb of tbs) tb.parentNode.removeChild(tb);
}
function parseInput() {
  for (let i of selMats) {
    M[i].parseInput();
    M[i].fillTable();
  }
}
function echlon(drag = false) {
  let resultMat;
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].echlon();
    M[0].genTable("echlon");
  } else {
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        resultMat = M[i].copyMat(true);
        resultMat.echlon();
        resultMat.genTable("echlon");
      }
    }
  }
}
function transpose(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].transpose().genTable("transpose");
  } else
    for (let i of selMats)
      if (M[i].cRow > 0) M[i].transpose().genTable("transpose");
}
function inverse(drag = false) {
  let resultMat;
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    resultMat = M[0].inverse();
    if (resultMat) resultMat.genTable("inverse");
  } else {
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        resultMat = M[i].inverse();
        if (resultMat) resultMat.genTable("inverse" + M[i].name);
      }
    }
  }
}
function _M1AddSubM2(m1, m2, op) {
  if (m1.cCol != m2.cCol || m1.cRow != m2.cRow) {
    alert("unmatched m1 m2");
    return;
  }
  let result = new Mat(m1.cRow, m1.cCol);
  if (op == "+") {
    for (let i = 0; i < result.cRow; i++)
      for (let j = 0; j < result.cCol; j++)
        result.data[i][j] = Mat._eAdd(m1.data[i][j], m2.data[i][j]);
  }
  if (op == "-") {
    for (let i = 0; i < result.cRow; i++)
      for (let j = 0; j < result.cCol; j++)
        result.data[i][j] = Mat._eSub(m1.data[i][j], m2.data[i][j]);
  }
  return result;
}
function M1AddM2(m1 = M[1], m2 = M[2]) {
  let result = _M1AddSubM2(m1, m2, "+");
  result.genTable("M1+M2");
}
function M1SubM2(m1 = M[1], m2 = M[2]) {
  let result = _M1AddSubM2(m1, m2, "-");
  result.genTable("M1-M2");
}
function M1MulM2(m1 = M[1], m2 = M[2]) {
  if ((M[0] = m1.matMultiply(m2)) != null) M[0].genTable("M1*M2");
}
function M2MulM1(m1 = M[1], m2 = M[2]) {
  if ((M[0] = m2.matMultiply(m1)) != null) M[0].genTable("M2*M1");
}
function matMultiply(m1 = M[1], m2 = M[2]) {
  if ((M[0] = m1.matMultiply(m2)) != null) M[0].genTable("M1*M2");
}
function M1JoinM2(m1 = M[1], m2 = M[2]) {
  let resultMat = m1.copyMat(true);
  resultMat.joinMat(m2);
  resultMat.genTable("M1~M2");
}
function M1M2M3Mul(cMat = 3) {
  let result = new MatIN(M[1].cRow); //to initiate zeros
  for (let i = 1; i < 4; i++) result = result.matMultiply(M[i]);
  result.genTable("M1*M2*M3");
}
function M1Power() {
  let elePow = document.querySelector("#pow");
  let pow = Number(elePow.value);
  M[0] = new MatIN(M[1].cRow);
  elePow.addEventListener("change", (e) => (pow = Number(e.target.value)));
  for (let i = 0; i < pow; i++) M[0] = M[0].matMultiply(M[1]);
  M[0].genTable("power M1 to " + pow);
}
function Axb_Inv(drag = false) {
  if (drag) {
    alert("solve x for M[1]x=M[2], drag not supported");
    return;
  }
  if (M[2].cCol != 1) {
    alert("M2 should be a column vector");
    return;
  }
  let result = M[1].inverse();
  if (result) result.matMultiply(M[2]).genTable("x");
}
function Axb_LUP(drag = false) {
  if (drag) {
    alert("solve x for M1*x=M2, drag not supported");
    return;
  }
  if (M[2].cCol != 1) {
    alert("M2 should be a column vector");
    return;
  }
  M[1].solveAxEqualB_LUFac({ b: M[2], showLUTable: false });
}
function LUFactorization(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].name = "";
    M[0].parseInput();
    M[0].LUFactorization();
  } else {
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        resultMat = M[i].copyMat(true);
        resultMat.name = "M" + i;
        resultMat.LUFactorization();
      }
    }
  }
}
function QRFactorization(drag = false) {
  let Q;
  let R;
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].name = "";
    M[0].parseInput();
    [Q, R] = M[0].QRFactorization();
    Q.genTable("Q");
    R.genTable("R");
  } else {
    for (let i of selMats) {
      [Q, R] = M[i].QRFactorization();
      Q.genTable(`M${i} Q`);
      R.genTable(`M${i} R`);
    }
  }
}
function DetRec(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    let result = new MatIN(1);
    result.data[0][0] = M[0].DetRec();
    result.genTable("Determinant");
  } else
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        let result = new MatIN(1);
        result.data[0][0] = M[i].DetRec();
        result.genTable("Determinant of M" + i);
      }
    }
}
function DetByLU(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    let result = new MatIN(1);
    result.data[0][0] = M[0].DetByLU();
    result.genTable("Determinant");
  } else
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        let result = new MatIN(1);
        result.data[0][0] = M[i].DetByLU();
        result.genTable("Determinant of M" + i);
      }
    }
}
function adjugate(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0] = M[0].adjugate();
    M[0].genTable("Adjugate");
  } else
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        let result = M[i].adjugate();
        result.genTable("Adjugate of M" + i);
      }
    }
}
async function findEigenvalue(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    let result;
    result = await M[0].findEigenvalue();
    if (!(result instanceof Error)) {
      result.genTable("Eigenvalue");
      M[0].findEigenvector();
      M[0].eigenVectors.genTable("Eigenvectors");
      M[0].numericEigenvectors.genTable("Numeric eigenvectors");
      M[0].eigenvalueDiagonal.genTable("Eigenvalue Diagonal");
    } else alert(result.message);
  } else {
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        result = await M[i].findEigenvalue();
        if (!(result instanceof Error)) {
          result.genTable("M" + i + " Eigenvalue");
          M[i].findEigenvector();
          M[i].eigenVectors.genTable("M" + i + " Eigenvectors");
          M[i].numericEigenvectors.genTable("M" + i + " Eigenvectors");
          M[i].eigenvalueDiagonal.genTable("M" + i + " Eigenvalue Diagonal");
        } else alert(result.message);
      }
    }
  }
}
async function SVD(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    let result = await M[0].SVD();
    if (!(result instanceof Error)) {
      M[0].leftSV.genTable("U (left singular vector)");
      M[0].SV.genTable("Σ (Singular value)");
      M[0].rightSV.genTable("VT (right singular vector)");
    } else alert(result.message);
  } else {
    for (let i of selMats) {
      if (M[i].cRow > 0) {
        let result = await M[i].SVD();
        if (!(result instanceof Error)) {
          M[i].leftSV.genTable("U (left singular vector) of M" + i);
          M[i].SV.genTable("Σ (singular value) of M" + i);
          M[i].rightSV.genTable("VT (right singular vector) of M" + i);
        } else alert(result.message);
      }
    }
  }
}

function splitIntoVectors(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].vectorize();
    for (let i = 0; i < M[0].vectors.length; i++)
      M[0].vectors[i].genTable(`vector ${i + 1}`);
  } else
    for (let i of selMats) {
      M[i].parseInput();
      M[i].vectorize();
      for (let j = 0; j < M[i].vectors.length; j++)
        M[i].vectors[j].genTable(`M${i} vector ${j + 1} `);
    }
}
function innerProduct(drag = false) {
  let result = new Mat(1);
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].vectorize();
    result.data[0][0] = M[0].vectors[0].selfInnerProduct();
    result.genTable("self innerProduct");
    return result.data[0][0];
  } else if (selMats.length == 2) {
    let [i, j] = [selMats[0], selMats[1]];
    M[i].vectorize();
    M[j].vectorize();
    result.data[0][0] = M[i].vectors[0].innerProduct(M[j].vectors[0]);
    result.genTable("innerProduct");
    return result.data[0][0];
  } else {
    alert("select two matrics");
  }
}
function selfInnerProduct(drag = false) {
  let result;
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].vectorize();
    result = new Mat(1, M[0].cCol);
    for (j = 0; j < M[0].cCol; j++)
      result.data[0][j] = M[0].vectors[j].selfInnerProduct();
    result.genTable("self innerProduct");
  } else
    for (let i of selMats) {
      M[i].parseInput();
      M[i].vectorize();
      result = new Mat(1, M[i].cCol);
      for (j = 0; j < M[i].cCol; j++)
        result.data[0][j] = M[i].vectors[j].selfInnerProduct();
      result.genTable("self innerProduct of M" + i);
    }
}
function vectorLength(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].vectorize();
    let result = new Mat(1, M[0].cCol);
    for (let j = 0; j < M[0].cCol; j++)
      result.data[0][j] = M[0].vectors[j].vectorLength();
    result.genTable("vector length");
  } else {
    for (let i of selMats) {
      M[i].parseInput();
      M[i].vectorize();
      let result = new Mat(1, M[i].cCol);
      for (j = 0; j < M[i].cCol; j++)
        result.data[0][j] = M[i].vectors[j].vectorLength();
      result.genTable("vector length of M" + i);
    }
  }
}
function matNormalize(drag = false) {
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    M[0].matNormalize(true);
    M[0].genTable(`normalized`);
  } else
    for (let i of selMats) {
      let resultMat = M[i].copyMat(true);
      resultMat.matNormalize(true);
      resultMat.genTable(`normalized M${i}`);
    }
}
function V1ProjOnV2() {
  if (selMats.length == 2) {
    let [i, j] = [selMats[0], selMats[1]];
    M[i].vectorize();
    M[j].vectorize();
    let result = M[i].vectors[0].projectOnVec(M[j].vectors[0]);
    result.genTable("projected vector");
  }
}
function V2ProjOnV1() {
  if (selMats.length == 2) {
    let [i, j] = [selMats[1], selMats[0]];
    M[i].vectorize();
    M[j].vectorize();
    let result = M[i].vectors[0].projectOnVec(M[j].vectors[0]);
    result.genTable("projected vector");
  }
}
function orthogonalBasis(drag = false) {
  let result;
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    result = M[0].orthogonalBasis();
    result.genTable("orthogonalBasis");
  } else
    for (let i of selMats) {
      result = M[i].orthogonalBasis();
      result.genTable("orthogonalBasis");
    }
}
function orthonormalBasis(drag = false) {
  let result;
  if (drag) {
    document.querySelector("#input0").value = dragSrc;
    M[0].parseInput();
    result = M[0].orthonormalBasis();
    result.genTable("orthonormalBasis");
  } else
    for (let i of selMats) {
      result = M[i].orthonormalBasis();
      result.genTable("orthonormalBasis");
    }
}
function drawVectors() {
  M[2].drawVectors();
}
function drawShape() {
  M[2].drawShape();
}
function vectorTransform2d() {
  let result = M[1].matMultiply(M[2]);
  result.genTable("vectorTransform2d");
  changeColor();
  result.drawShape();
}

function setInitValue() {
  // document.getElementById("input1").value = `\\sqrt{4}`;
  document.getElementById("input1").value = `√6`;
  console.log(document.getElementById("input1").value);
  katex.render(
    document.getElementById("input1").value,
    document.querySelector("#divEditor"),
    {
      throwOnError: false,
    }
  );
}
